package org.NooLab.randomgraph.components;

import java.util.ArrayList;


import org.math.array.StatisticSample;

import org.NooLab.randomgraph.RandomGraphFactory;
import org.NooLab.randomgraph.properties.RGraphProperties;
import org.NooLab.randomgraph.storage.RGraphDataBase;
import org.NooLab.randomgraph.storage.RGraphFingerPrint;
import org.NooLab.structures.randomgraph.FingerPrintMatrixIntf;
import org.NooLab.utilities.strings.ArrUtilities;

import org.math.array.StatisticSample ;
import org.math.array.LinearAlgebra ;

/**
 
    Principles of dimensionality reduction via Matrix Multiplication
  
        RxC     RxC       concat'd
	A = 2x3     5x30  ->  1x150      num of columns in A
                                   		==  
	R = 3x2     30x8                 num of row in R, while num of col in R 
                                   		== 
	T = 2x2     5x8   ->  1x40       num of col in T
-------------------------------------

        RxC     RxC       
	A = 2x3     1x150       num of columns in A
                            	==  
	R = 3x2     150x40      num of row in R, while num of col in R 
                              	== 
	T = 2x2     1x40        num of col in T

  =>=>=>=>=> we have to work on data BEFORE concatenation
             the factor in the examples above is 6000/240 = 25 !!
             

- dynamic task:  R(A) 3..7
                 C(A) 20..500

  source matrix A reflects a context made from R positions
  count of columns in source matrix could be very large,
    dependent on methods to describe a single word
 
  actually, it could comprise multiple descriptions 
 
  
  
 * 
 *
 */
public class RandomProjection {

	RandomGraphProcessor randomGraphProcessor;
	RandomGraphFactory rgFactory;
	private RGraphDataBase rgDatabase;
	RGraphProperties rgProperties;
	int finLen ;
	
	FingerPrintMatrix fpMatrix;
	
	ArrayList<ArrayList<Double>> inMatrix = new ArrayList<ArrayList<Double>> () ; 
	ArrayList<Double> compressedVector = new ArrayList<Double> ();
	
	StatisticSample sampler ;
	String wordLabel;
	int centerIndex;
	
	
	// ========================================================================
	public RandomProjection(RandomGraphProcessor rgProcessor) {
		
		randomGraphProcessor = rgProcessor;
		rgFactory = randomGraphProcessor.rgFactory ;
		rgProperties = (RGraphProperties) rgFactory.getRgProperties() ;
		rgDatabase = randomGraphProcessor.rgDatabase;
		finLen = rgProperties.getLengthResultVector() ; 
		
		// test();
	}

	// ========================================================================


	private void test() {
		/*
		
		A = input matrix with long rows, 
		R = random matrix
		T = target matrix with lesser number of rows
		 
          A       R        T  		 
		1 2 3     6 -1     12 -6
		4 5 6     3  2     39 -12
		          0 -3 
		*/
		
		double[][] A,R,T;
		A = new double[2][3];
		R = new double[3][2];
		T = new double[2][2];
		
		A[0][0] = 1 ;
		A[0][1] = 2 ;
		A[0][2] = 3 ;
		A[1][0] = 4 ;
		A[1][1] = 5 ;
		A[1][2] = 6 ;
		

		R[0][0] = 6 ;
		R[0][1] = -1 ;
		R[1][0] = 3 ;
		R[1][1] = 2 ;
		R[2][0] = 0 ;
		R[2][1] = -3 ;
		
		T = LinearAlgebra.matrixMult(A,R) ;
		
		double v = T[1][1] ;
		
		
	}
	
	
	public void close() {
		for (int i=0;i<inMatrix.size();i++){
			inMatrix.get(i).clear(); 
		}
		inMatrix.clear();
		compressedVector.clear() ;
		if (fpMatrix!=null){
			fpMatrix.close();
			fpMatrix = null;
		}
	}



	/**
	 * InputMatrix should contain >1 row!!
	 */
	public void setInputMatrix( FingerPrintMatrixIntf fingerPrintMatrix) {
		int rc=0;
		
		if (fingerPrintMatrix.getFmatrix()!=null){
			rc = fingerPrintMatrix.getFmatrix().size();
		}else{
			return;
		}
		
		if ((rc>0) && (fpMatrix==null)){
			fpMatrix = new FingerPrintMatrix( rc );
		}
		fpMatrix.clear();
		fpMatrix.addAll( fingerPrintMatrix );
		
	}


	public void compress() throws Exception {
		boolean rB=false;
		int rcA,ccA, ccR,rcR , rcT,ccT;
		String storLabel = "" ;
		
		ArrayList<ArrayList<Double>> randomMatrix ;
		double[][] randomMatrixA , inMatrixA, projectedMatrix;
		
		RGraphFingerPrint rgFP ;
		
		
		// ................................................
		
		inMatrix = fpMatrix.fmatrix;
		
		// get dimensions, target is finLen
		rcA = inMatrix.size() ;
			  if (rcA==0){
				  throw(new Exception("fpMatrix is empty"));
			  }
		ccA = inMatrix.get(0).size() ;
			  if (rcA==0){
				  throw(new Exception("rows of fpMatrix are empty"));
			  }
		
		ccR = (int)Math.round((double)finLen/(double)rcA);
		rcR = ccA;	
		
		inMatrixA = changeMatrixDataType( inMatrix);
		
		// now get a matrix of random numbers for the word "wordLabel"
		// we use the words global index in fingerprint table to set the seed
		// such we get reproducible random numbers
		
		rgFP = rgDatabase.getFingerprint(wordLabel,storLabel);
		Long k = rgFP.getWordid() ;
		
		sampler = new StatisticSample(102341 + k); 
		double rndval = sampler.getNextUniformRandom();
		
		randomMatrixA = createRandomMatrixA( rcR,ccR );
		
		// now multiply...
		
		projectedMatrix = LinearAlgebra.matrixMult(inMatrixA, randomMatrixA) ;
						
		rcT = projectedMatrix.length;
		ccT = projectedMatrix[0].length;
		
		prepareCompressedVector(projectedMatrix);
	}
	
	
	private void prepareCompressedVector(double[][] projectedMatrix) {
		int rcT,ccT;
		double max ;
		
		rcT = projectedMatrix.length;
		ccT = projectedMatrix[0].length;
	
		// ArrayList<Double> 
		compressedVector.clear();
		
		for (int r=0;r<rcT;r++){
			for (int c=0;c<ccT;c++){
				
				compressedVector.add( projectedMatrix[r][c] );
				
			} // c->
		} // r->
		
	
		compressedVector = ArrUtilities.arrayNormalize(compressedVector) ;
	}
	
	
	
	private double[][] createRandomMatrixA(int rc, int cc) {
		
		double[][] rMatrix = new double[rc][cc];
		double rndval;
		
		for (int r=0;r<rc;r++){
			
			for (int c=0;c<cc;c++){
				rndval = sampler.getNextUniformRandom();
				rMatrix[r][c] = rndval ; 
			} // c->
		} // r->
		
		return rMatrix;
	}
	
	private ArrayList<ArrayList<Double>> createRandomMatrix(int rc, int cc) {
		
		ArrayList<ArrayList<Double>>  rMatrix = new ArrayList<ArrayList<Double>> () ;
		double rndval;
		
		for (int r=0;r<rc;r++){
			
			ArrayList<Double> row = new ArrayList<Double>();
			rMatrix.add(row) ;
			
			for (int c=0;c<cc;c++){
				rndval = sampler.getNextUniformRandom();
				row.add(rndval) ;
			} // c->
		} // r->
		
		return rMatrix;
	}
	/*
  
        RxC     RxC       concat'd
	A = 2x3     5x30  ->  1x150      num of columns in A
                                   		==  
	R = 3x2     30x8                 num of row in R, while num of col in R 
                                   		== 
	T = 2x2     5x8   ->  1x40       num of col in T
 
 
	 */
	
	private double[][] changeMatrixDataType(ArrayList<ArrayList<Double>> matrix) {
		
		int rc,cc;
		double v;
		double[][] rMatrix  ;
		ArrayList<Double> mr ;
		
		rc = matrix.size();
		
		cc = matrix.get(0).size();
		rMatrix = new double[rc][cc];
		
		
		try{
		

			for (int r=0;r<rc;r++){
				cc = matrix.get(0).size();
				for (int c=0;c<cc;c++){

					v=0;
					mr = matrix.get(r);
					if (mr.size()>0){
						v = mr.get(c);
					}else{
						
					}
					rMatrix[r][c] = v ;
				} // c->
			} // r->


			
		}catch(Exception e){
			System.out.println("problem in RandomProjection::changeMatrixDataType(), wordlabel: "+ wordLabel); 
			e.printStackTrace();
		}
				
		return rMatrix;
	}



	public ArrayList<Double> getCompressedVector() {
		return compressedVector;
	}

	public void setWordLabel(String wordStr) {
		wordLabel = wordStr ;
	}

	public void setCenterIndex(int ival) {
		centerIndex = ival;
	}
	
	
}
